package nape.phys;
import cx.FastList;
import cx.MixList;
import cx.Algorithm;
import nape.phys.PhysObj;
import nape.phys.Properties;
import nape.geom.VecMath;
import nape.geom.Vec2;
import nape.shape.Shape;
import nape.Const;
import nape.space.UniformSpace;
import nape.space.UniformSleepSpace;





















class Body extends PhysObj {
	
	public var bw:Float; 
	
	vec_new(public,gl); 
	vec_new(public,gp); 
	
	
	
	
	public var shapes:CxMixList(Shape);
	
	
	public var uniformsleepcells:CxFastList(UniformSleepCell);
	
	
	
	public function new(?x:Float=0,?y:Float=0,?properties:Properties) {
		super(x,y,true,properties);
		body = this;
		a = w = t = bw = 0;
		shapes = new CxMixList(Shape)();
		vec_set(rot,0,1);
	}
	
	
	
	public inline function setAngle(angle:Float):Void {
		a = angle;
		vec_set(rot,Math.sin(a),Math.cos(a));
		vec_rotate(gl,rot,gp);
	}
	
	public override function update() {
		updatePosition(0);
		CxIter(s,shapes,s.updateShape());
	}
	
	
	
	public inline function updateVelocity(dt:Float) {
		updateVelocity_linear(dt);
		if(smoment!=0.0) {
			w = w * properties.afdt + t * dt * imoment;
		}
		t = 0;
	}
	public inline function updatePosition(dt:Float) {
		aabb.minx = aabb.miny = Const.POSINF;
		aabb.maxx = aabb.maxy = -Const.POSINF;
		
		updatePosition_linear(dt);
		pre_a = a;
		if(smoment!=0.0) {
			a += w * dt + bw;
			bw = 0;
		}
		vec_set(rot,Math.sin(a),Math.cos(a));
		vec_rotate(gl,rot,gp);
		if(hasGraphic && rotateGraphic)
			graphic.rotation = (a*Const.RAD_TO_DEG)%360;
	}
	
	
	
	public inline function addShape(s:Shape) {
		if(s.body!=this) {
			if(s.body!=null)
				s.body.removeShape(s);
			s.body = this;
			shapes.add(s);
			return true;
		}else return false;
	}
	public function removeShape(s:Shape) {
		if(shapes.remove(s)) {
			s.body = null;
			
			return true;
		}else return false;
	}
	
	
	
	public function calcProperties():Void {
		var m = 0.;
		var i = 0.;
		evrad = 0.;
		CxIter(s,shapes,{
			var sm = s.area * s.material.density;
			m += sm;
			if(s.type==Shape.CIRCLE) i += s.circle .inertia() * sm;
			else                     i += s.polygon.inertia() * sm;
			if(s.type==Shape.CIRCLE) evrad += s.circle .inertia();
			else                     evrad += s.polygon.inertia();
		});
		if (m > 0) { mass    = m;          imass    = 1 / m; }
		else       { mass    = Const.FMAX; imass    = 0;     }
		if (i > 0) { moment  = i;          imoment  = 1 / i; }
		else       { moment  = Const.FMAX; imoment  = 0;     }
		cmass = smass = imass;
		cmoment = smoment = imoment;
		gmass = mass;
		
		var p = COM(); vec_set(gl,p.p);
		vec_set(gp,gl);
	}
	
	
	
	public inline function ROTX(v:Vec2) return vec_cross(v.p,rot)
	public inline function ROTY(v:Vec2) return vec_dot  (v.p,rot)
	
	public inline function ROTXs(vx:Float,vy:Float) return vec_cross(v,rot)
	public inline function ROTYs(vx:Float,vy:Float) return vec_dot  (v,rot)
	
	
	
	public inline function stopRotation() {
		moment = Const.POSINF;
		w = t = bw = 0;
		imoment = smoment = 0;
		#if flash9 if(hasGraphic) graphic.cacheAsBitmap = true; #end
	}
	public inline function allowRotation() {
		moment = 1.0/cmoment;
		imoment = smoment = cmoment;
		#if flash9 if(hasGraphic && rotateGraphic) graphic.cacheAsBitmap = false; #end
	}
	
	
	
	public inline function COM():Vec2 {
		var ret = new Vec2(0,0);
		var msum = 0.0;
		CxIter(s,shapes,{
			var sm = s.area * s.material.density;
			if(s.type == Shape.CIRCLE) vec_addeq(ret.p,s.circle.offset.p,sm);
			else                       vec_addeq(ret.p,s.polygon.COM().p,sm);
			msum += sm;
		});
		vec_diveq(ret.p,msum);
		return ret;
	}
	
	public inline function shift(x:Vec2) {
		CxIter(s,shapes,{
			if(s.type == Shape.CIRCLE) s.circle .shift(x);
			else                       s.polygon.shift(x);
		});
		vec_addeq(gl,x.p);
	}
	
	public inline function align() {
		var x = COM();
		x.px = -x.px;
		x.py = -x.py;
		shift(x);
	}
	
}